Foundation Of Quickdraw
Volume Number: 1
Issue Number: 2
Column Tag: features of Mac Pascal
QuickDraw Graphics
By Chris Derossi
One of the most valuable features of Mac Pascal is it ability to take advantage of
the Macintosh’s powerful ROM capabilities. To do this, Mac Pascal must be able to
access the various routines that are contained within the Mac Toolbox. In this month’s
column, we will examine the interface between Mac Pascal and the ROM, and the
necessary data structures for accessing quickdraw. Finally, a sample program is
included which makes use of quickdraw’s graphics routines.
In order to communicate to the Toolbox, several data structures have to be
defined. These data structures are all presented in Pascal format because the routines
contained in the Mac ROM are constructed for a Pascal environment. Unlike other
programming languages, this makes data compatiblitly pretty much straight forward
for Mac Pascal users. But before we get into the details, let’s briefly describe some of
the theory behind QuickDraw.
FOUNDATION OF QUICKDRAW
QuickDraw graphics exist in cartesian coordinate planes. What this means is that
graphics may be present on any portion of a continuous surface. The QuickDraw planes
are finite, and extend from values -32768 to 32767 in both the vertical and
horizontal directions. The smallest unit of distance that QuickDraw can work with is 1.
An area of 1 by 1 is, therefore, a point. The boundary separating two points at
sequential locations of the plane is of size zero. This means that two points placed next
to each other are continuous, and have no space between them. Keep in mind that these
are theoretical concepts, not screen bit-map specifications.
QUICKDRAW DATA STRUCTURES
In Mac Pascal, the data structure ‘Point’ is a record with two components
representing the coordinates of the upper left corner of a point. The two fields of Point
are Point.h and Point.v which are the horizontal and vertical coordinates respectively.
Each value is of type Integer.
The rectangle is a common object found in QuickDraw. Rectangles are frequently
used to create windows, define drawing areas, and to provide parameters for
manipualting other objects. A rectangle has four parameters. These are: Top, Left,
Bottom, and Right. These parameters may be thought of as four integers, or as the two
points TopLeft and BottomRight. In actuality, the second method is used, and the type
‘Rect’ is defined as a record with the two fields Rect.TopLeft and Rect.BotRight. Each of
the fields is of type point.
Another object defined with points is the line. Although there is no Mac Pascal
data type for this object, it can be characterized by two points, for the beginning and
ending positions of the line.
The final data type we will examine this month is the pattern. QuickDraw allows
regions to be filled with patterns, as is done with the paint bucket in MacPaint. A
pattern is a set of sixty-four points either on or off, arranged in an eight by eight
square. To fill an area larger than eight by eight, the Macintosh repeats the pattern
both vertically and horizontally as many times as neccessary. The upper left corner of
the pattern is aligned with the coordinate (0,0), and is independent of the coordinates
of the area being filled.
Since a byte is eight bits, and a bit represents one point, patterns can be
represented as a series of eight bytes. In Mac Pascal, the type ‘Pattern’ is an array
[0..7] of 0..255, which is effectively eight sequential bytes. There are some
predefined patterns including White, Black, and Grey.
DRAWING IN QUICKDRAW
The drawing mechanism in QuickDraw is the Pen. The pen has modifyable
properties which include location, width, height, and pattern. There are further
properties which will be covered in a later column. A pen of width and height of one,
and a pattern of black will always draw with one-point wide, black lines.
In addition to the many objects of QuickDraw, many actions are available to be
performed on or with them. Most objects may be Framed, Painted, Filled, and Erased.
The QuickDraw actions are called QuickDraw verbs, and they are applied to objects.
The verb Frame specifies that the outline of the given object should be drawn. The verb
Paint means that the object is drawn completely filled. Each of these actions use the
pen for drawing, and as such use the current pen pattern. The verb Erase does not use
the pen pattern, but instead uses the pattern currently set for the background. Fill
allows you to specify a pattern to use, and is therefore a little bit more versatile than
Paint.
For example, FrameRect will draw the outline of a rectangle, while PaintRect
will draw a solid rectangle. FillRect will draw a solid rectangle in the given pattern.
EraseRect clears the given rectangle to the current background pattern.
Each verb-object combination is a procedure, and takes applicable parameters.
The Rect procedures take a rectangle as their only parameter. The FillRect procedure
needs a Rect first, and a Pattern second. The rectangle is drawn as a solid with the
pattern.
A frequently encountered situation is one where the Macintosh needs to know if a
given point is inside a particular rectangle. (i.e. is the mouse’s position inside a
certain window?) To satisfy this need, a function titled PtInRect returns a boolean of
True if the point is inside the rectangle, and False if not. The parameters of this
function are a point first, and then a rectangle.
PROGRAMMING EXAMPLE
Now let us examine the example program that makes use of these data structures,
procedures, and functions. The operation of the program is fairly simple and straight
forward. The user is presented with a blank background and the mouse pointer. The
user forms rectangles by pressing the mouse button when the pointer is at one of the
rectangle’s corners, and holds the button down while dragging the mouse to the opposite
diagonal corner. In order to provide some feedback about the shape of the rectangle, a
‘rubber banding’ diagonal line follows the mouse until the button is released. When the
button is released, the rubber band line is removed, and the rectangle is drawn.
WHERE’S THE MOUSE?
After the rectangle is drawn, the user may place the mouse pointer inside the
rectangle, and press the button. As long as the button is held, the Mac continually fills
the rectangle with random, changing patterns. Once the mouse button is pressed outside
of the rectangle, the pattern is frozen, and a new rectangle is begun. (Please see the
sample output in fig. 1.)
The construction of the program is not complex. The variable declarations for the
main program consist of three points and one rectangle. The points are used as work
variables for reading the mouse location, and the rectangle is the one currently being
drawn or filled.
The main program calls two procedures. The first one allows the user to create
the rectangle, and provides the rubberbanding effect. The second one creates random
patterns and fills the rectangle.
Since the value of a rectangle’s Top must be less than or equal to that of its
bottom, two procedures called ‘Min’ and ‘Max’ help keep the points in the right order.
The same holds true for the Left and Right values.
The key procedure calls in the Make_Rect procedure are Button, GetMouse,
DrawLine, PenPat, FrameRect, and InsetRect. The function Button returns True if the
mouse button is pressed, and False if not. GetMouse uses two VAR parameters for
obtaining the current horizontal and vertical position of the mouse pointer.
Since we need to draw and erase the rubberbanding line, we use the two pen
patterns of Black and White. This has the disadvantage of erasing whatever the line
passes over, but you can’t have everything in a simple little program. The procedure
PenPat sets the working pen pattern to the passed value.
DrawLine uses two points, this time in the form of four coordinates, to designate
the starting and ending points of the line. DrawLine, of course, uses the current pen
pattern.
Finally, after the mouse button is released, the rectangle is drawn with
FrameRect. This is done with the pen pattern set to black. Since we don’t want to draw
over the outline with subsequent FillRect calls, the procedure InsetRect is called to
change the coordinates of the rectangle by one on each side, making it slightly smaller.
After the rectangle is drawn, the Fill_Rect procedure is called to fill it in. Note,
please, that Fill_Rect is distinct from FillRect. Fill_Rect uses the calls Button,
GetMouse, PtInRect, Random, and FillRect to perform its task. Button and GetMouse are
used as before. PtInRect determines if the mouse is inside the new rectangle when the
button is depressed.
A new pattern is created by calling the function Random eight times within a loop.
Random returns a value from -32768 and 32767. Abs and Mod are used to bring the
random number in the range 0..255. After the random pattern is created, FillRect is
called to draw the pattern into the rectangle.
MODIFYING THE PROGRAM
The program runs forever, and the menu option Halt must be chosen to exit. In
order to overcome this, you might want to modify the program to present you with a
Black or Grey box (read rectangle) where you’d click the mouse to quit. Where in the
program would you draw the rectangle? At what point or points would you have to
check to see if it has been clicked? How will you handle rectangles that overlap your
‘exit’ box? These exercises are left to the student!
That’s it for this month. In the next column, we’ll further explore the powerful
features of QuickDraw, and find out what makes it so unique and wonderful among
graphic tools. We’ll also have another program that utilizes these terrific features. If
you like our Pascal column or have programming ideas of your own to contribute,
please feel free to write me care of MacTech. Ciao.
program MacTutor_QD_Demo;
{ This program will use the standard features of } {QuickDraw to
introduce use of QuickDraw data} {structures and routine calls from
MacPascal.}
{ -- By Chris Derossi}
var
P1, P2, P3 : Point;
WorkingRect : Rect;
function Min (Num1, Num2 : integer) : integer;
{ return the minimum of the two numbers passed.}
begin
if Num1 < Num2 then
Min := Num1
else
Min := Num2;
end;
function Max (Num1, Num2 : integer) : integer;
{ returns the maximum of two numbers passed.}
begin
if Num1 > Num2 then
Max := Num1
else
Max := Num2;
end;
procedure Make_Rect (var theRect : Rect);
{ This is the first of the two main procedure. It}
{allows the user to select diagonal corners of the}
{rectangle to be drawn, using a ‘rubber band’ line.}
{When the button is released, the recatangle is}
{drawn.}
begin
repeat
until Button; {Wait till the button is pressed}
GetMouse(P1.h, P1.v); {Where was the mouse?}
P2 := P1;
P3 := P1;
while Button do {update while button is down.}
begin
GetMouse(P3.h, P3.v);
if (P2.h <> P3.h) or (P2.v <> p3.v) then
{the mouse has moved}
begin
PenPat(White);
DrawLine(P1.h, P1.v, P2.h, P2.v);
{Erase the old line}
PenPat(Black);
DrawLine(P1.h, P1.v, P3.h, P3.v);
{Draw the new line}
P2 := P3;
end;
end;
PenPat(White); {Erase line for the last time.}
DrawLine(P1.h, P1.v, P2.h, P2.v);
PenPat(Black);
theRect.TopLeft.v := Min(P1.v, P2.v);
{Set the rectangle, making sure}
theRect.TopLeft.h := Min(P1.h, P2.h);
{that the top is above the bottom}
theRect.BotRight.v := Max(P1.v, P2.v);
{and the left is less than the}
theRect.BotRight.h := Max(P1.h, P2.h); {right.}
FrameRect(theRect); {Draw the rectangle}
InsetRect(theRect, 1, 1);
{Shrink it so that the pattern does not draw}
{over the rectangle frame.}
end;
procedure Fill_Rect (theRect : Rect);
var
myPat : Pattern;
Index : Integer;
Done : Boolean;
begin
Done := False;
while not Done do
{continue until the mouse is outside of the}
{new rectangle.}
begin
repeat
until Button; {Wait for the button}
GetMouse(P1.h, P1.v);
if PtInRect(P1, theRect) then
{is the mouse in the rectangle?}
begin
for Index := 0 to 7 do
myPat[Index] := Abs(Random) mod 256; {make a random pattern}
FillRect(theRect, myPat); {Fill the rectangle}
end
else
Done := True;
end; {While}
end; {Fill_Rect}
begin {Main Program}
while true do
begin
Make_Rect(WorkingRect);
Fill_Rect(WorkingRect);
end;
end. {Main}
